/*
 * 版权所有 (C) 2015 知启蒙(ZHIQIM) 保留所有权利。[遇见知启蒙，邂逅框架梦]
 * 
 * https://zhiqim.org/project/zhiqim_framework/zhiqim_kernel.htm
 *
 * Zhiqim Kernel is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
package org.zhiqim.kernel.model.seqs;

import java.io.RandomAccessFile;

import org.zhiqim.kernel.constants.CodeConstants;
import org.zhiqim.kernel.util.Asserts;
import org.zhiqim.kernel.util.Files;
import org.zhiqim.kernel.util.Ints;
import org.zhiqim.kernel.util.Longs;
import org.zhiqim.kernel.util.Strings;
import org.zhiqim.kernel.util.Validates;

/**
 * 整型序列值，默认从1从0x7FFFFFFF(最大值)，并支持保存文件<br><br>
 * 1、最小值必须>=1<br>
 * 2、如果设置最大值，则最大值必须大小最小值，否则取整数最大值0x7FFFFFFF<br>
 * 3、是否自动保存
 * 
 * @version v1.0.0 @author zouzhigang 2014-2-27 新建与整理
 */
public class SequenceFile extends Sequence implements CodeConstants
{
    private final boolean auto;
    private final String filePath;
    private final RandomAccessFile file;
    private final boolean isValid;
    
    /***********************************************************************************/
    //默认自动加载
    /***********************************************************************************/
    
    /** 从1到0x7FFFFFFF，自动加载 */
    public SequenceFile(String filePath)
    {
        this(1, Integer.MAX_VALUE, 10, 1, filePath, true);
    }
    
    /** 从1到len个9，自动加载 */
    public SequenceFile(int len, String filePath)
    {
        this(1, Ints.getMaxInt(len), len, 1, filePath, true);
    }
    
    /** 从min到max，自动加载 */
    public SequenceFile(long min, long max, String filePath)
    {
        this(min, max, Longs.length(max), min, filePath, true);
    }
    
    /** 从min到max，指定初始值，自动加载 */
    public SequenceFile(long min, long max, long seq, String filePath)
    {
        this(min, max, Longs.length(max), seq, filePath, true);
    }
    
    /***********************************************************************************/
    //设置自动/手动加载，一般用于手动加载
    /***********************************************************************************/
    
    /** 从1到0x7FFFFFFF */
    public SequenceFile(String filePath, boolean auto)
    {
        this(1, Integer.MAX_VALUE, 10, 1, filePath, auto);
    }
    
    /** 从1到指定长度的len个9 */
    public SequenceFile(int len, String filePath, boolean auto)
    {
        this(1, Ints.getMaxInt(len), len, 1, filePath, auto);
    }
    
    /** 从min到max */
    public SequenceFile(long min, long max, String filePath, boolean auto)
    {
        this(min, max, Longs.length(max), min, filePath, auto);
    }
    
    /** 从min到max，指定初始值 */
    public SequenceFile(long min, long max, long seq, String filePath, boolean auto)
    {
        this(min, max, Longs.length(max), seq, filePath, auto);
    }
    
    /***********************************************************************************/
    //全属性构造函数
    /***********************************************************************************/
    
    /** 本类和子类可见 */
    protected SequenceFile(long min, long max, int len, long seq, String filePath, boolean auto)
    {
        super(min, max, len, seq);
        
        this.filePath = filePath;
        this.auto = auto;
        
        String fileDir = Files.getFileDir(filePath);
        if (!Files.mkDirectory(fileDir))
        {//检查文件路径失败时不抛异常，防止NoClassDefineError
            file = null;
            isValid = false;
        }
        else
        {//文件正常，加载初始值
            file = Files.randomNewRW(this.filePath).result();
            if (auto)
            {//自动加载，=false时，通过new SequenceFile(10, filePath, false).load();方式加载
                load();
            }
            isValid = true;
        }
    }

    public String getFilePath()
    {
        return filePath;
    }

    public boolean isAuto()
    {
        return auto;
    }
    
    public boolean isValid()
    {
        return isValid;
    }
    
    protected String readFile()
    {
        return Files.readUTF8(filePath);
    }
    
    protected void writeFile(String str)
    {
        Files.randomWriteUTF8(file, 0, str);
    }
    
    /********************************************************************/
    //加载和保存序列号，自动标志打开时，新建即加载，每次next即保存
    /********************************************************************/
    
    /** 加载序列号 */
    public SequenceFile load()
    {
        if (!Files.isFile(filePath))
            return this;
        
        String seq = Strings.trim(readFile());
        if (!Validates.isIntegerPositive(seq))
            return this;
        
        setSequence(Longs.toLong(seq));
        return this;
    }
    
    /** 保存序列号 */
    public void save()
    {//19位最大长整型，不足后补空格，保证每次写入时完整
        writeFile(Strings.suffixLen(sequence, 19, _SPACE_));
    }
    
    /********************************************************************/
    //重写获取并保存
    /********************************************************************/
    
    @Override
    public long next()
    {
        if (!isValid)
        {//创建就失败，但不在构造函数中报错，而在使用时报异常
            throw Asserts.exception("创建SequenceFile失败，文件目录不存在且创建失败");
        }
        
        if (!auto)
        {//无需自动保存
            return super.next();
        }
        
        synchronized (this)
        {//自动保存要求保存成功再返回（保存下一个，返回当前）
            chkSequence();
            long seq = sequence++;
            save();
            return seq;
        }
    }
}
